using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using JGSY.CMS.LowCode.Platform.Application.DTOs;
using JGSY.CMS.LowCode.Platform.Application.Interfaces;
using JGSY.CMS.LowCode.Platform.Domain.Entities;
using System.Text.Json;

namespace JGSY.CMS.LowCode.Platform.Infrastructure.Services
{
    /// <summary>
    /// 增强的审计服务实现
    /// 提供丰富的审计功能和上下文收集
    /// </summary>
    public class EnhancedAuditService : IEnhancedAuditService
    {
        private readonly IAuditLogAppService _auditLogService;
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly ILogger<EnhancedAuditService> _logger;

        public EnhancedAuditService(
            IAuditLogAppService auditLogService,
            IHttpContextAccessor httpContextAccessor,
            ILogger<EnhancedAuditService> logger)
        {
            _auditLogService = auditLogService;
            _httpContextAccessor = httpContextAccessor;
            _logger = logger;
        }

        /// <summary>
        /// 记录权限拒绝访问审计日志
        /// </summary>
        public async Task<AuditLog> LogPermissionDeniedAsync(
            string userId,
            string permissionCode,
            string requestPath,
            string httpMethod,
            string reason,
            string? tenantId = null,
            string? correlationId = null,
            CancellationToken cancellationToken = default)
        {
            var context = _httpContextAccessor.HttpContext;
            var ipAddress = GetClientIpAddress(context);
            var userAgent = context?.Request.Headers["User-Agent"].ToString();
            
            var auditLog = new AuditLog
            {
                Id = DateTime.UtcNow.Ticks,
                Type = "PermissionDenied",
                Content = $"Permission denied for accessing {permissionCode}: {reason}",
                UserId = userId,
                Timestamp = DateTime.UtcNow,
                IP = ipAddress,
                Result = "Access Denied",
                Resource = permissionCode,
                TenantId = tenantId ?? string.Empty,
                UserAgent = userAgent ?? string.Empty,
                Duration = null,
                CorrelationId = correlationId ?? Guid.NewGuid().ToString(),
                HttpMethod = httpMethod,
                RequestPath = requestPath,
                StatusCode = 403,
                PermissionCode = permissionCode,
                ExtendedData = JsonSerializer.Serialize(new 
                { 
                    reason = reason,
                    ipAddress = ipAddress,
                    timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss")
                })
            };

            try
            {
                var auditDto = new AuditLogDto
                {
                    Id = auditLog.Id,
                    Type = auditLog.Type,
                    Content = auditLog.Content,
                    UserId = auditLog.UserId,
                    UserName = string.Empty, // 可以从用户服务获取
                    Timestamp = auditLog.Timestamp,
                    IP = auditLog.IP,
                    Result = auditLog.Result,
                    Resource = auditLog.Resource,
                    TenantId = auditLog.TenantId,
                    UserAgent = auditLog.UserAgent,
                    Duration = auditLog.Duration,
                    CorrelationId = auditLog.CorrelationId,
                    HttpMethod = auditLog.HttpMethod,
                    RequestPath = auditLog.RequestPath,
                    StatusCode = auditLog.StatusCode,
                    PermissionCode = auditLog.PermissionCode,
                    ExtendedData = auditLog.ExtendedData
                };

                await _auditLogService.AddAsync(auditDto);
                _logger.LogWarning("Permission denied for user {UserId} accessing {Resource} - {Reason}", 
                    userId, permissionCode, reason);
                return auditLog;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to log permission denied audit");
                throw;
            }
        }

        /// <summary>
        /// 记录业务操作审计日志
        /// </summary>
        public async Task<AuditLog> LogBusinessOperationAsync(
            string userId,
            string operationType,
            string operationName,
            string targetType,
            string targetId,
            Dictionary<string, object>? operationData = null,
            string? tenantId = null,
            string? correlationId = null,
            CancellationToken cancellationToken = default)
        {
            var context = _httpContextAccessor.HttpContext;
            var ipAddress = GetClientIpAddress(context);
            var userAgent = context?.Request.Headers["User-Agent"].ToString();
            
            var auditLog = new AuditLog
            {
                Id = DateTime.UtcNow.Ticks,
                Type = operationType,
                Content = $"Business operation: {operationName} on {targetType} {targetId}",
                UserId = userId,
                Timestamp = DateTime.UtcNow,
                IP = ipAddress,
                Result = "Success",
                Resource = targetId,
                TenantId = tenantId ?? string.Empty,
                UserAgent = userAgent ?? string.Empty,
                Duration = null,
                CorrelationId = correlationId ?? Guid.NewGuid().ToString(),
                HttpMethod = context?.Request.Method,
                RequestPath = context?.Request.Path,
                StatusCode = 200,
                ExtendedData = JsonSerializer.Serialize(new 
                { 
                    operationType = operationType,
                    operationData = operationData,
                    targetType = targetType,
                    ipAddress = ipAddress,
                    timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss")
                })
            };

            try
            {
                var auditDto = new AuditLogDto
                {
                    Id = auditLog.Id,
                    Type = auditLog.Type,
                    Content = auditLog.Content,
                    UserId = auditLog.UserId,
                    UserName = string.Empty,
                    Timestamp = auditLog.Timestamp,
                    IP = auditLog.IP,
                    Result = auditLog.Result,
                    Resource = auditLog.Resource,
                    TenantId = auditLog.TenantId,
                    UserAgent = auditLog.UserAgent,
                    Duration = auditLog.Duration,
                    CorrelationId = auditLog.CorrelationId,
                    HttpMethod = auditLog.HttpMethod,
                    RequestPath = auditLog.RequestPath,
                    StatusCode = auditLog.StatusCode,
                    ExtendedData = auditLog.ExtendedData
                };

                await _auditLogService.AddAsync(auditDto);
                _logger.LogInformation("Business operation logged for user {UserId}: {OperationName} on {TargetType}",
                    userId, operationName, targetType);
                return auditLog;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to log business operation audit");
                throw;
            }
        }

        /// <summary>
        /// 记录数据变更审计日志
        /// </summary>
        public async Task<AuditLog> LogDataChangeAsync(
            string userId,
            string entityType,
            string entityId,
            string changeType,
            Dictionary<string, object>? oldValues = null,
            Dictionary<string, object>? newValues = null,
            string? tenantId = null,
            string? correlationId = null,
            CancellationToken cancellationToken = default)
        {
            var context = _httpContextAccessor.HttpContext;
            var ipAddress = GetClientIpAddress(context);
            var userAgent = context?.Request.Headers["User-Agent"].ToString();
            
            var auditLog = new AuditLog
            {
                Id = DateTime.UtcNow.Ticks,
                Type = changeType,
                Content = $"Data change: {changeType} on {entityType} {entityId}",
                UserId = userId,
                Timestamp = DateTime.UtcNow,
                IP = ipAddress,
                Result = "Success",
                Resource = entityId,
                TenantId = tenantId ?? string.Empty,
                UserAgent = userAgent ?? string.Empty,
                Duration = null,
                CorrelationId = correlationId ?? Guid.NewGuid().ToString(),
                HttpMethod = context?.Request.Method,
                RequestPath = context?.Request.Path,
                StatusCode = 200,
                ExtendedData = JsonSerializer.Serialize(new 
                { 
                    changeType = changeType,
                    entityType = entityType,
                    oldValues = oldValues,
                    newValues = newValues,
                    ipAddress = ipAddress,
                    timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss")
                })
            };

            try
            {
                var auditDto = new AuditLogDto
                {
                    Id = auditLog.Id,
                    Type = auditLog.Type,
                    Content = auditLog.Content,
                    UserId = auditLog.UserId,
                    UserName = string.Empty,
                    Timestamp = auditLog.Timestamp,
                    IP = auditLog.IP,
                    Result = auditLog.Result,
                    Resource = auditLog.Resource,
                    TenantId = auditLog.TenantId,
                    UserAgent = auditLog.UserAgent,
                    Duration = auditLog.Duration,
                    CorrelationId = auditLog.CorrelationId,
                    HttpMethod = auditLog.HttpMethod,
                    RequestPath = auditLog.RequestPath,
                    StatusCode = auditLog.StatusCode,
                    ExtendedData = auditLog.ExtendedData
                };

                await _auditLogService.AddAsync(auditDto);
                _logger.LogInformation("Data change logged for user {UserId}: {ChangeType} on {EntityType} {EntityId}",
                    userId, changeType, entityType, entityId);
                return auditLog;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to log data change audit");
                throw;
            }
        }

        /// <summary>
        /// 记录性能监控审计日志
        /// </summary>
        public async Task<AuditLog> LogPerformanceAsync(
            string userId,
            string operationName,
            long duration,
            Dictionary<string, object>? performanceMetrics = null,
            string? tenantId = null,
            string? correlationId = null,
            CancellationToken cancellationToken = default)
        {
            var context = _httpContextAccessor.HttpContext;
            var ipAddress = GetClientIpAddress(context);
            var userAgent = context?.Request.Headers["User-Agent"].ToString();
            
            var auditLog = new AuditLog
            {
                Id = DateTime.UtcNow.Ticks,
                Type = "PerformanceLog",
                Content = $"Performance log: {operationName} took {duration}ms",
                UserId = userId,
                Timestamp = DateTime.UtcNow,
                IP = ipAddress,
                Result = "Completed",
                Resource = operationName,
                TenantId = tenantId ?? string.Empty,
                UserAgent = userAgent ?? string.Empty,
                Duration = duration,
                CorrelationId = correlationId ?? Guid.NewGuid().ToString(),
                HttpMethod = context?.Request.Method,
                RequestPath = context?.Request.Path,
                StatusCode = 200,
                ExtendedData = JsonSerializer.Serialize(new 
                { 
                    operationName = operationName,
                    duration = duration,
                    performanceMetrics = performanceMetrics,
                    ipAddress = ipAddress,
                    timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss")
                })
            };

            try
            {
                var auditDto = new AuditLogDto
                {
                    Id = auditLog.Id,
                    Type = auditLog.Type,
                    Content = auditLog.Content,
                    UserId = auditLog.UserId,
                    UserName = string.Empty,
                    Timestamp = auditLog.Timestamp,
                    IP = auditLog.IP,
                    Result = auditLog.Result,
                    Resource = auditLog.Resource,
                    TenantId = auditLog.TenantId,
                    UserAgent = auditLog.UserAgent,
                    Duration = auditLog.Duration,
                    CorrelationId = auditLog.CorrelationId,
                    HttpMethod = auditLog.HttpMethod,
                    RequestPath = auditLog.RequestPath,
                    StatusCode = auditLog.StatusCode,
                    ExtendedData = auditLog.ExtendedData
                };

                await _auditLogService.AddAsync(auditDto);
                _logger.LogInformation("Performance logged for user {UserId}: {OperationName} took {Duration}ms",
                    userId, operationName, duration);
                return auditLog;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to log performance audit");
                throw;
            }
        }

        /// <summary>
        /// 记录系统事件审计日志
        /// </summary>
        public async Task<AuditLog> LogSystemEventAsync(
            string eventType,
            string eventName,
            Dictionary<string, object>? eventData = null,
            string? userId = null,
            string? tenantId = null,
            string? correlationId = null,
            CancellationToken cancellationToken = default)
        {
            var context = _httpContextAccessor.HttpContext;
            var ipAddress = GetClientIpAddress(context);
            var userAgent = context?.Request.Headers["User-Agent"].ToString();
            
            var auditLog = new AuditLog
            {
                Id = DateTime.UtcNow.Ticks,
                Type = eventType,
                Content = $"System event: {eventName}",
                UserId = userId ?? "System",
                Timestamp = DateTime.UtcNow,
                IP = ipAddress,
                Result = "Event",
                Resource = eventName,
                TenantId = tenantId ?? string.Empty,
                UserAgent = userAgent ?? string.Empty,
                Duration = null,
                CorrelationId = correlationId ?? Guid.NewGuid().ToString(),
                HttpMethod = context?.Request.Method,
                RequestPath = context?.Request.Path,
                StatusCode = 200,
                ExtendedData = JsonSerializer.Serialize(new 
                { 
                    eventType = eventType,
                    eventName = eventName,
                    eventData = eventData,
                    ipAddress = ipAddress,
                    timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss")
                })
            };

            try
            {
                var auditDto = new AuditLogDto
                {
                    Id = auditLog.Id,
                    Type = auditLog.Type,
                    Content = auditLog.Content,
                    UserId = auditLog.UserId,
                    UserName = string.Empty,
                    Timestamp = auditLog.Timestamp,
                    IP = auditLog.IP,
                    Result = auditLog.Result,
                    Resource = auditLog.Resource,
                    TenantId = auditLog.TenantId,
                    UserAgent = auditLog.UserAgent,
                    Duration = auditLog.Duration,
                    CorrelationId = auditLog.CorrelationId,
                    HttpMethod = auditLog.HttpMethod,
                    RequestPath = auditLog.RequestPath,
                    StatusCode = auditLog.StatusCode,
                    ExtendedData = auditLog.ExtendedData
                };

                await _auditLogService.AddAsync(auditDto);
                _logger.LogInformation("System event logged: {EventType} - {EventName}", eventType, eventName);
                return auditLog;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to log system event audit");
                throw;
            }
        }

        /// <summary>
        /// 批量记录审计日志
        /// </summary>
        public async Task<int> LogBatchAsync(
            IEnumerable<AuditLog> auditLogs,
            CancellationToken cancellationToken = default)
        {
            if (auditLogs == null || !auditLogs.Any())
            {
                return 0;
            }

            try
            {
                var count = 0;
                foreach (var log in auditLogs)
                {
                    var auditDto = new AuditLogDto
                    {
                        Id = log.Id,
                        Type = log.Type,
                        Content = log.Content,
                        UserId = log.UserId,
                        UserName = log.UserName,
                        Timestamp = log.Timestamp,
                        IP = log.IP,
                        Result = log.Result,
                        Resource = log.Resource,
                        TenantId = log.TenantId,
                        UserAgent = log.UserAgent,
                        Duration = log.Duration,
                        CorrelationId = log.CorrelationId,
                        HttpMethod = log.HttpMethod,
                        RequestPath = log.RequestPath,
                        StatusCode = log.StatusCode,
                        PermissionCode = log.PermissionCode,
                        ExtendedData = log.ExtendedData
                    };

                    await _auditLogService.AddAsync(auditDto);
                    count++;
                }

                _logger.LogInformation("Batch logged {Count} audit entries", count);
                return count;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to log batch audit");
                throw;
            }
        }

        /// <summary>
        /// 获取客户端IP地址
        /// </summary>
        private string GetClientIpAddress(HttpContext? context)
        {
            if (context == null) return "Unknown";

            // 检查反向代理头
            var forwarded = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
            if (!string.IsNullOrEmpty(forwarded))
            {
                return forwarded.Split(',')[0].Trim();
            }

            var realIp = context.Request.Headers["X-Real-IP"].FirstOrDefault();
            if (!string.IsNullOrEmpty(realIp))
            {
                return realIp;
            }

            // 使用直连IP
            return context.Connection.RemoteIpAddress?.ToString() ?? "Unknown";
        }
    }
}
